package com.seafood.service.impl;

import com.seafood.dto.FileUploadDTO;
import com.seafood.dto.FileUploadRequestDTO;
import com.seafood.dto.FileUploadResponseDTO;
import com.seafood.entity.FileUpload;
import com.seafood.repository.FileUploadRepository;
import com.seafood.service.FileUploadService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 文件上传服务实现
 */
@Slf4j
@Service
@RequiredArgsConstructor
@Transactional
public class FileUploadServiceImpl implements FileUploadService {
    
    private final FileUploadRepository fileUploadRepository;
    
    @Value("${app.upload.path:/uploads}")
    private String uploadPath;
    
    @Value("${app.upload.url:/files/}")
    private String uploadUrl;

    
    @Value("${app.upload.domain:http://localhost:8080}")
    private String domain;
    
    @Value("${app.upload.max-size:10485760}") // 10MB
    private Long maxFileSize;
    
    private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    
    // 允许的图片类型
    private static final Set<String> ALLOWED_IMAGE_TYPES = Set.of(
            "image/jpeg", "image/jpg", "image/png", "image/gif", "image/webp"
    );
    
    // 允许的文档类型
    private static final Set<String> ALLOWED_DOCUMENT_TYPES = Set.of(
            "application/pdf", "application/msword", 
            "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
            "application/vnd.ms-excel",
            "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
    );
    
    @Override
    public FileUploadResponseDTO uploadFile(MultipartFile file, FileUploadRequestDTO requestDTO, 
                                          Long uploaderId, String uploaderName) {
        
        // 验证文件
        validateFile(file, requestDTO.getBusinessType());
        
        try {
            // 计算文件MD5
            String fileMd5 = DigestUtils.md5DigestAsHex(file.getBytes());
            
            // 检查文件是否已存在（去重）
            Optional<FileUpload> existingFile = fileUploadRepository.findByFileMd5AndIsDeletedFalse(fileMd5);
            if (existingFile.isPresent()) {
                log.info("文件已存在，返回已有文件信息: md5={}", fileMd5);
                return convertToResponseDTO(existingFile.get());
            }
            
            // 生成存储文件名
            String storedFilename = generateStoredFilename(file.getOriginalFilename());
            
            // 创建存储目录
            String datePath = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd"));
            String businessPath = requestDTO.getBusinessType().toLowerCase();
            String fullPath = uploadPath + "/" + businessPath + "/" + datePath;
            
            File directory = new File(fullPath);
            if (!directory.exists()) {
                directory.mkdirs();
            }
            
            // 保存文件
            String filePath = fullPath + "/" + storedFilename;
            File destFile = new File(filePath);
            file.transferTo(destFile);
            
            // 生成文件URL
            String fileUrl = uploadUrl + businessPath + "/" + datePath + "/" + storedFilename;
            
            // 保存文件记录
            FileUpload fileUpload = new FileUpload();
            fileUpload.setOriginalFilename(file.getOriginalFilename());
            fileUpload.setStoredFilename(storedFilename);
            fileUpload.setFilePath(filePath);
            fileUpload.setFileUrl(fileUrl);
            fileUpload.setFileSize(file.getSize());
            fileUpload.setContentType(file.getContentType());
            fileUpload.setFileType(determineFileType(file.getContentType()));
            fileUpload.setBusinessType(FileUpload.BusinessType.valueOf(requestDTO.getBusinessType().toUpperCase()));
            fileUpload.setBusinessId(requestDTO.getBusinessId());
            fileUpload.setUploaderId(uploaderId);
            fileUpload.setUploaderName(uploaderName);
            fileUpload.setFileMd5(fileMd5);
            fileUpload.setIsPublic(requestDTO.getIsPublic());
            fileUpload.setDescription(requestDTO.getDescription());
            fileUpload.setTags(requestDTO.getTags());
            
            // 生成缩略图（如果是图片且需要）
            if (requestDTO.getGenerateThumbnail() && fileUpload.isImage()) {
                String thumbnailUrl = generateThumbnailForImage(filePath, fileUrl, 
                        requestDTO.getThumbnailWidth(), requestDTO.getThumbnailHeight());
                fileUpload.setThumbnailUrl(thumbnailUrl);
            }
            
            FileUpload saved = fileUploadRepository.save(fileUpload);
            log.info("文件上传成功: filename={}, size={}, path={}", 
                    file.getOriginalFilename(), file.getSize(), filePath);
            
            return convertToResponseDTO(saved);
            
        } catch (IOException e) {
            log.error("文件上传失败: filename={}, error={}", file.getOriginalFilename(), e.getMessage());
            throw new RuntimeException("文件上传失败: " + e.getMessage());
        }
    }
    
    @Override
    public List<FileUploadResponseDTO> uploadFiles(List<MultipartFile> files, FileUploadRequestDTO requestDTO, 
                                                  Long uploaderId, String uploaderName) {
        return files.stream()
                .map(file -> uploadFile(file, requestDTO, uploaderId, uploaderName))
                .collect(Collectors.toList());
    }
    
    @Override
    @Transactional(readOnly = true)
    public FileUploadDTO getFileById(Long id) {
        FileUpload fileUpload = fileUploadRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("文件不存在"));
        
        if (fileUpload.getIsDeleted()) {
            throw new RuntimeException("文件已被删除");
        }
        
        return convertToDTO(fileUpload);
    }
    
    @Override
    @Transactional(readOnly = true)
    public List<FileUploadDTO> getFilesByBusiness(String businessType, Long businessId) {
        FileUpload.BusinessType type = FileUpload.BusinessType.valueOf(businessType.toUpperCase());
        List<FileUpload> files = fileUploadRepository.findByBusinessTypeAndBusinessIdAndIsDeletedFalse(type, businessId);
        return files.stream().map(this::convertToDTO).collect(Collectors.toList());
    }
    
    @Override
    @Transactional(readOnly = true)
    public Page<FileUploadDTO> getFilesByUploader(Long uploaderId, Pageable pageable) {
        Page<FileUpload> files = fileUploadRepository.findByUploaderIdAndIsDeletedFalse(uploaderId, pageable);
        return files.map(this::convertToDTO);
    }
    
    @Override
    @Transactional(readOnly = true)
    public Page<FileUploadDTO> getFilesByType(String fileType, Pageable pageable) {
        FileUpload.FileType type = FileUpload.FileType.valueOf(fileType.toUpperCase());
        Page<FileUpload> files = fileUploadRepository.findByFileTypeAndIsDeletedFalse(type, pageable);
        return files.map(this::convertToDTO);
    }
    
    @Override
    @Transactional(readOnly = true)
    public Page<FileUploadDTO> getPublicFiles(Pageable pageable) {
        Page<FileUpload> files = fileUploadRepository.findByIsPublicTrueAndIsDeletedFalse(pageable);
        return files.map(this::convertToDTO);
    }
    
    @Override
    @Transactional(readOnly = true)
    public Page<FileUploadDTO> searchFiles(String keyword, Pageable pageable) {
        Page<FileUpload> files = fileUploadRepository.searchByKeyword(keyword, pageable);
        return files.map(this::convertToDTO);
    }
    
    @Override
    @Transactional(readOnly = true)
    public Page<FileUploadDTO> getPopularFiles(Pageable pageable) {
        Page<FileUpload> files = fileUploadRepository.findPopularFiles(pageable);
        return files.map(this::convertToDTO);
    }
    
    @Override
    @Transactional(readOnly = true)
    public Page<FileUploadDTO> getRecentFiles(Pageable pageable) {
        Page<FileUpload> files = fileUploadRepository.findRecentFiles(pageable);
        return files.map(this::convertToDTO);
    }
    
    @Override
    public byte[] downloadFile(Long fileId) {
        FileUpload fileUpload = fileUploadRepository.findById(fileId)
                .orElseThrow(() -> new RuntimeException("文件不存在"));
        
        if (fileUpload.getIsDeleted()) {
            throw new RuntimeException("文件已被删除");
        }
        
        try {
            Path filePath = Paths.get(fileUpload.getFilePath());
            byte[] fileBytes = Files.readAllBytes(filePath);
            
            // 增加下载次数
            fileUpload.incrementDownloadCount();
            fileUploadRepository.save(fileUpload);
            
            return fileBytes;
        } catch (IOException e) {
            throw new RuntimeException("文件读取失败: " + e.getMessage());
        }
    }
    
    @Override
    public void deleteFile(Long fileId, Long operatorId) {
        FileUpload fileUpload = fileUploadRepository.findById(fileId)
                .orElseThrow(() -> new RuntimeException("文件不存在"));
        
        fileUpload.setIsDeleted(true);
        fileUploadRepository.save(fileUpload);
        
        log.info("文件已被删除: fileId={}, operatorId={}", fileId, operatorId);
    }
    
    @Override
    public void deleteFiles(List<Long> fileIds, Long operatorId) {
        fileIds.forEach(fileId -> deleteFile(fileId, operatorId));
    }
    
    @Override
    public void physicalDeleteFile(Long fileId) {
        FileUpload fileUpload = fileUploadRepository.findById(fileId)
                .orElseThrow(() -> new RuntimeException("文件不存在"));
        
        try {
            // 删除物理文件
            Files.deleteIfExists(Paths.get(fileUpload.getFilePath()));
            
            // 删除缩略图
            if (fileUpload.getThumbnailUrl() != null) {
                String thumbnailPath = fileUpload.getFilePath().replace(
                        fileUpload.getStoredFilename(), 
                        "thumb_" + fileUpload.getStoredFilename());
                Files.deleteIfExists(Paths.get(thumbnailPath));
            }
            
            // 删除数据库记录
            fileUploadRepository.delete(fileUpload);
            
            log.info("文件物理删除成功: fileId={}", fileId);
        } catch (IOException e) {
            log.error("文件物理删除失败: fileId={}, error={}", fileId, e.getMessage());
            throw new RuntimeException("文件删除失败: " + e.getMessage());
        }
    }
    
    @Override
    public FileUploadDTO updateFileInfo(Long fileId, String description, String tags, Boolean isPublic) {
        FileUpload fileUpload = fileUploadRepository.findById(fileId)
                .orElseThrow(() -> new RuntimeException("文件不存在"));
        
        if (description != null) fileUpload.setDescription(description);
        if (tags != null) fileUpload.setTags(tags);
        if (isPublic != null) fileUpload.setIsPublic(isPublic);
        
        FileUpload updated = fileUploadRepository.save(fileUpload);
        return convertToDTO(updated);
    }
    
    @Override
    public String generateThumbnail(Long fileId, Integer width, Integer height) {
        FileUpload fileUpload = fileUploadRepository.findById(fileId)
                .orElseThrow(() -> new RuntimeException("文件不存在"));
        
        if (!fileUpload.isImage()) {
            throw new RuntimeException("只有图片文件才能生成缩略图");
        }
        
        return generateThumbnailForImage(fileUpload.getFilePath(), fileUpload.getFileUrl(), width, height);
    }
    
    @Override
    @Transactional(readOnly = true)
    public FileUploadResponseDTO checkFileExists(String fileMd5) {
        Optional<FileUpload> existingFile = fileUploadRepository.findByFileMd5AndIsDeletedFalse(fileMd5);
        return existingFile.map(this::convertToResponseDTO).orElse(null);
    }
    
    @Override
    @Transactional(readOnly = true)
    public Map<String, Object> getFileStatistics() {
        Map<String, Object> statistics = new HashMap<>();
        
        // 文件类型统计
        List<Object[]> typeStats = fileUploadRepository.countByFileType();
        Map<String, Long> typeCount = new HashMap<>();
        for (Object[] row : typeStats) {
            typeCount.put(row[0].toString(), (Long) row[1]);
        }
        
        // 业务类型统计
        List<Object[]> businessStats = fileUploadRepository.countByBusinessType();
        Map<String, Long> businessCount = new HashMap<>();
        for (Object[] row : businessStats) {
            businessCount.put(row[0].toString(), (Long) row[1]);
        }
        
        // 总文件大小
        Long totalSize = fileUploadRepository.getTotalFileSize();
        
        statistics.put("typeStatistics", typeCount);
        statistics.put("businessStatistics", businessCount);
        statistics.put("totalFileSize", totalSize != null ? totalSize : 0L);
        statistics.put("totalFileCount", fileUploadRepository.count());
        
        return statistics;
    }
    
    @Override
    @Transactional(readOnly = true)
    public Map<String, Object> getUserFileStatistics(Long uploaderId) {
        Map<String, Object> statistics = new HashMap<>();
        
        Long totalSize = fileUploadRepository.getTotalFileSizeByUploader(uploaderId);
        statistics.put("totalFileSize", totalSize != null ? totalSize : 0L);
        
        return statistics;
    }
    
    @Override
    public void cleanupOrphanFiles() {
        List<FileUpload> orphanFiles = fileUploadRepository.findOrphanFiles();
        log.info("发现孤立文件数量: {}", orphanFiles.size());
        
        for (FileUpload file : orphanFiles) {
            if (file.getCreatedAt().isBefore(LocalDateTime.now().minusDays(7))) {
                physicalDeleteFile(file.getId());
                log.info("已清理孤立文件: {}", file.getOriginalFilename());
            }
        }
    }
    
    @Override
    public boolean isAllowedFileType(String contentType, String businessType) {
        if (contentType == null) return false;
        
        switch (businessType.toUpperCase()) {
            case "PRODUCT_IMAGE":
            case "USER_AVATAR":
            case "COMPANY_LOGO":
                return ALLOWED_IMAGE_TYPES.contains(contentType.toLowerCase());
            case "CERTIFICATE":
            case "INSPECTION_REPORT":
            case "TRACEABILITY_DOC":
                return ALLOWED_DOCUMENT_TYPES.contains(contentType.toLowerCase()) || 
                       ALLOWED_IMAGE_TYPES.contains(contentType.toLowerCase());
            default:
                return ALLOWED_IMAGE_TYPES.contains(contentType.toLowerCase()) || 
                       ALLOWED_DOCUMENT_TYPES.contains(contentType.toLowerCase());
        }
    }
    
    @Override
    public boolean isAllowedFileSize(Long fileSize, String businessType) {
        if (fileSize == null || fileSize <= 0) return false;
        
        // 各业务类型的文件大小限制
        Map<String, Long> sizeLimit = Map.of(
            "USER_AVATAR", 2 * 1024 * 1024L,      // 2MB
            "PRODUCT_IMAGE", 5 * 1024 * 1024L,    // 5MB
            "CERTIFICATE", 10 * 1024 * 1024L,     // 10MB
            "INSPECTION_REPORT", 20 * 1024 * 1024L // 20MB
        );
        
        Long limit = sizeLimit.getOrDefault(businessType.toUpperCase(), maxFileSize);
        return fileSize <= limit;
    }
    
    /**
     * 验证文件
     */
    private void validateFile(MultipartFile file, String businessType) {
        if (file.isEmpty()) {
            throw new RuntimeException("文件不能为空");
        }
        
        if (!isAllowedFileType(file.getContentType(), businessType)) {
            throw new RuntimeException("不支持的文件类型");
        }
        
        if (!isAllowedFileSize(file.getSize(), businessType)) {
            throw new RuntimeException("文件大小超过限制");
        }
    }
    
    /**
     * 生成存储文件名
     */
    private String generateStoredFilename(String originalFilename) {
        String extension = "";
        if (originalFilename != null && originalFilename.contains(".")) {
            extension = originalFilename.substring(originalFilename.lastIndexOf("."));
        }
        return UUID.randomUUID().toString().replace("-", "") + extension;
    }
    
    /**
     * 根据内容类型判断文件类型
     */
    private FileUpload.FileType determineFileType(String contentType) {
        if (contentType == null) return FileUpload.FileType.OTHER;
        
        if (contentType.startsWith("image/")) {
            return FileUpload.FileType.IMAGE;
        } else if (contentType.startsWith("video/")) {
            return FileUpload.FileType.VIDEO;
        } else if (contentType.startsWith("audio/")) {
            return FileUpload.FileType.AUDIO;
        } else if (contentType.contains("pdf") || 
                   contentType.contains("word") || 
                   contentType.contains("excel") ||
                   contentType.contains("document") ||
                   contentType.contains("spreadsheet")) {
            return FileUpload.FileType.DOCUMENT;
        } else if (contentType.contains("zip") || 
                   contentType.contains("rar") ||
                   contentType.contains("archive")) {
            return FileUpload.FileType.ARCHIVE;
        } else {
            return FileUpload.FileType.OTHER;
        }
    }
    
    /**
     * 为图片生成缩略图（模拟实现）
     */
    private String generateThumbnailForImage(String filePath, String fileUrl, Integer width, Integer height) {
        // 这里是模拟实现，实际项目中可以使用图片处理库
        // 例如: ImageIO, Thumbnailator 等
        log.info("模拟生成缩略图: filePath={}, size={}x{}", filePath, width, height);
        
        // 返回模拟的缩略图 URL
        return fileUrl.replace(".jpg", "_thumb.jpg")
                     .replace(".png", "_thumb.png")
                     .replace(".jpeg", "_thumb.jpeg");
    }
    
    /**
     * 实体转 DTO
     */
    private FileUploadDTO convertToDTO(FileUpload entity) {
        FileUploadDTO dto = new FileUploadDTO();
        BeanUtils.copyProperties(entity, dto);
        
        dto.setFileType(entity.getFileType().name());
        dto.setBusinessType(entity.getBusinessType().name());
        dto.setCreatedAt(entity.getCreatedAt() != null ? entity.getCreatedAt().format(FORMATTER) : null);
        dto.setUpdatedAt(entity.getUpdatedAt() != null ? entity.getUpdatedAt().format(FORMATTER) : null);
        
        return dto;
    }
    
    /**
     * 实体转响应 DTO
     */
    private FileUploadResponseDTO convertToResponseDTO(FileUpload entity) {
        FileUploadResponseDTO dto = new FileUploadResponseDTO();
        dto.setFileId(entity.getId());
        dto.setOriginalFilename(entity.getOriginalFilename());
        dto.setFileUrl(entity.getFileUrl());
        dto.setThumbnailUrl(entity.getThumbnailUrl());
        dto.setFileSize(entity.getFileSize());
        dto.setContentType(entity.getContentType());
        dto.setFileType(entity.getFileType().name());
        dto.setBusinessType(entity.getBusinessType().name());
        dto.setBusinessId(entity.getBusinessId());
        dto.setFileMd5(entity.getFileMd5());
        dto.setIsPublic(entity.getIsPublic());
        dto.setUploadTime(entity.getCreatedAt() != null ? entity.getCreatedAt().format(FORMATTER) : null);
        
        return dto;
    }
}

